/****************************************************************************
 *			SRLOS Team All Right Reserved									*
 *																			*
 *breaf: this file is the main program of srlos boot						*
 *autor: bloceanc															*
 *e-mail: bloceanc@gmail.com												*
 *																			*
 *																			*
 ****************************************************************************/
#ifndef _BOOT_C
#define	_BOOT_C

#include "boot.h"

extern void RdNF2SDRAM(void);

/*
 *	clock_pll_init
 */
void clock_pll_init(void)
{
	// When you set MPLL&UPLL values, you have to set the UPLL value first and then the MPLL value. (Needs intervals approximately 7 NOP)

	R_UPLLCON = (0x38<<12)|(0x2<<4)|(0x2);	// 48M
	__asm__ __volatile__(	\
	"nop\n\t"	\
	"nop\n\t"	\
	"nop\n\t"	\
	"nop\n\t"	\
	"nop\n\t"	\
	"nop\n\t"	\
	"nop\n\t"	\
	);
	R_MPLLCON = (0x5c<<12)|(0x1<<4)|0x1;	// 400M
	__asm__ __volatile__(	\
	"nop\n\t"	\
	"nop\n\t"	\
	"nop\n\t"	\
	"nop\n\t"	\
	"nop\n\t"	\
	"nop\n\t"	\
	"nop\n\t"	\
	);
}

/*
 * clock_init
 */
void clock_init(void)
{
	// first init PLL, use 400M, 48M mode
	clock_pll_init();
	// init FLCK, PLCK, HLCK
	// use 1:4:8 mode(0b0101)
	R_CLKDIVN = 0x5;
}

/*
 *	sdram_init
 */
void dram_init(void)
{
	R_BWSCON |= 0x02000000;		// use bank 6, (s3c2440 start address is 0x30000000)
	R_BANKCON6 = 0x00018008;
	R_REFRESH |= 1269;			// this value supported by manule, maybe error!
	R_BANKSIZE = 0xb1;
	R_MRSRB6 = 0x00000030;
}

void nandflash_reset(void);
/********************** NAND FLASH CONTROL **************/
/*
 *	Nand Flash Controls
 */
void nandflash_init(void)
{
	R_NFCONT = (1<<0);
	R_NFCONT |= (1<<1);
	nandflash_reset();
}

/*
 * 	Reset Nand Flash Control
 */
void nandflash_reset(void)
{
	R_NFCONT &= ~(1<<1);
	R_NFCMMD = 0xff;
	while(!(R_NFSTAT & 0x1));	// waiting for busy
	R_NFSTAT |= 1;
}

/*
 *	Nand Flash Controls
 * 	return: readed size
 */
unsigned int nandflash_read(char *buf, unsigned long address, unsigned int size)
{
	unsigned int read_size = 0;
	unsigned int i;
	
	// check address and size whether crosspounding the flash block size
	if(NAND_FLASH_BLOCK_MASK & address || NAND_FLASH_BLOCK_MASK & size)
		return -1;

	// enable chip select
	R_NFCONT &= ~(1<<1);
	
	for(i = address; i < (address + size);)
	{
		int j;
		
		R_NFSTAT |= (1<<2);
		
		// send read command
		R_NFCMMD = 0;
		
		// set address to NFADDR register
		R_NFADDR = i & 0xff;
		R_NFADDR = (i >> 9) & 0xff;
		R_NFADDR = (i >> 17) & 0xff;
		R_NFADDR = (i >> 25) & 0xff;
		
		// waiting controllor finished
		while(!(R_NFSTAT & 0x1));
		
		// read data
		for( j = 0; j < NAND_FLASH_BLOCK_SIZE; j++, i++)
		{
			*buf = R_NFDATA & 0xff;
			buf++;
			read_size++;
		}
	}
	
	// disable chip select
	R_NFCONT |= 1 << 1;
	
	return read_size;
}

//
//	SET DOMAIN 00 for SYSTEM ACCSESS!
//
#define	PAGE_DOMAIN_ACCDESS_SYSTEM	0x1

// 	define	AP S R for access contorl
#define	PAGE_AP_SYSTEM				0x1 
#define	PAGE_AP_USER				0x3

/*
 * 	Set FLPT(First Level Page Table) of OS
 * 	NOTE:	(0~512M in virtual address space!)
 * 	more information see designe documnet
 */
void mmu_flpt_init(void)
{
	unsigned int *pFLPT = (unsigned int *)MMU_PHYSICAL_ADDR_FLPT_MEM;
	unsigned int i = 0,j = 0;
	
	// initialize for memory of OS space
	for(i = 0; i < MMU_OS_FLPT_RCD_COUNT_MEM; i++)
	{
		pFLPT[i] = ((MMU_PHYSICAL_ADDR_SLPT_MEM + (i<<10))&0xfffffc00)|(PAGE_DOMAIN_ACCDESS_SYSTEM<<5)|(1<<4)|1;
	}
	
	// initialize for I/O mapping
	for(j = 0; j < MMU_OS_FLPT_RCD_COUNT_IO; j++,i++)
	{
		pFLPT[i] = ((MMU_PHYSICAL_ADDR_SLPT_IO + (j<<10))&0xfffffc00)|(PAGE_DOMAIN_ACCDESS_SYSTEM<<5)|(1<<4)|1;
	}
}

/*
 * 	Set SLPT(Second Level Page Table) of OS
 * 	NOTE: as FLPT on size of address space
 */
void mmu_slpt_init(void)
{
	unsigned int *pSLPT = (unsigned int *)MMU_PHYSICAL_ADDR_SLPT_MEM;
	unsigned int i,j;
	
	// fill memory mapping of OS space
	for( i = 0; i < MMU_OS_SLPT_RCD_COUNT_MEM; i++)
	{
		// enable cache!
		pSLPT[i] = ((SDRAM_START_ADDRESS + (i<<12))&0xfffff000)|(PAGE_AP_SYSTEM<<10)|(PAGE_AP_SYSTEM<<8)|
		(PAGE_AP_SYSTEM<<6)|(PAGE_AP_SYSTEM<<4)|(1<<3)|(1<<2)|2;
	}
	
	// fill IO mapping of OS space
	for( j = 0; j < MMU_OS_SLPT_RCD_COUNT_IO; j++, i++)
	{
		// disable cache!IO!
		pSLPT[i] = ((MMU_PHYSICAL_ADDR_IOMAPPING + (j<<12))&0xfffff000)|(PAGE_AP_SYSTEM<<10)|(PAGE_AP_SYSTEM<<8)|
		(PAGE_AP_SYSTEM<<6)|(PAGE_AP_SYSTEM<<4)|(0<<3)|(0<<2)|2;
	}
}

/*
 * 	Set MMU FLPT Address
 */
void mmu_set_flpt(unsigned int *flpt_addr)
{
	__asm__ __volatile__(		\
	"stmfd r13!,{r0-r12}\n\t"	\
	"ldr r0, %0\n\t"	\
	"mcr p15, 0, r0, c2, c0, 0\n\t"	\
	"ldmfd r13!,{r0-r12}\n\t"	\
	::"m"(flpt_addr)	\
	);
}

/*
 * 	Set MMU Domain
 * 	use 0x7(0b0111)	domain 0 means user mode. domain 1 means system mode
 */
void mmu_set_domain(void)
{
	__asm__ __volatile__(			\
	"stmfd r13!,{r0}\n\t"			\
	"mov r0, #0x7\n\t"				\
	"mcr p15, 0, r0, c3, c0, 0\n\t"	\
	"ldmfd r13!,{r0}\n\t"			\
	);
}

/*
 * 	Initialize MMU but don't enable it.
 * 	注意：根据s3c2440的manual,we must set Asynchronous bus mode.because we set cpu clock 400M, HCKL 100M! shit!...or CPU will slow to 100M
 * we set ROM protection bit 1, system bit 0.then if AP=00, this page is readonly for all including kernel
 */
void mmu_init(void)
{
	__asm__ __volatile__("stmfd r13!, {r0,r1}\n\t"	\
	"mrc p15, 0, r1, c1, c0, 0\n\t"	\
	"ldr r0, =0x0000527e\n\t"	\
	"orr r0, r0, r1\n\t"	\
	"mcr p15, 0, r0, c1, c0, 0\n\t"	\
	"mrc p15, 0, r0, c1, c0, 0\n\t"	\
	"ldr r1, =0xc0000000\n\t"	\
	"orr r0, r0, r1\n\t"		\
	"mcr p15, 0, r0, c1, c0, 0\n\t"	\
	"ldmfd r13!,{r0,r1}\n\t"	\
	);
}

/*
 * 	Copy Bootloader to SDRAM
 */
void copy_bootloader(void)
{
	unsigned int *boot_sdram_addr = (unsigned int *)MMU_PHYSICAL_ADDR_BOOTLOADER;
	unsigned int *boot_rom_addr = (unsigned int*)0x00000000;
	unsigned int boot_size_per4B = MMU_OS_BOOTER_SIZE >> 2;
	unsigned int i;
	for( i = 0; i < boot_size_per4B; i++)
	{
		boot_sdram_addr[i] = boot_rom_addr[i];
	}
}

/*
 * main C function of BOOT program!
 */
void init_main(void)
{
	unsigned int loaded_kernel_size = 0;
	
	// init clock
	clock_init();
	
	
	// initialize SDRAM, as we will load the system and move BOOT program and Create MMU memory page tables of OS
	dram_init();
	
	// initialize NAND FLASH CONTROLLOR
//	nandflash_init();
	
	// copy bootloader to sdram
	copy_bootloader();
	
	// read kernel from nand flash and load it to SDRAM, if failed, try and try!
/*	do
	{
		loaded_kernel_size = nandflash_read((char *)MMU_PHYSICAL_ADDR_KERNEL,NAND_FLASH_KERNEL_ADDRESS,NAND_FLASH_KERNEL_SIZE);
	}while(loaded_kernel_size != NAND_FLASH_KERNEL_SIZE);
*/
	// ...... i don;t want to understand nandflash operation now.just use test code.
	RdNF2SDRAM();
	// set FLPT address for MMU
	mmu_set_flpt((unsigned int *)MMU_PHYSICAL_ADDR_FLPT_MEM);
	
	// set Domain for MMU
	mmu_set_domain();
	
	// initialize MMU but don't enable it. Enable it in boot.s
	mmu_flpt_init();
	mmu_slpt_init();
	mmu_init();	

}

/** only used for testing mmu and others */
/*
void test_code(void)
{
	#define	IO_BASE	0x48000000
#define	MMU_VIRTUAL_ADDR_IOMAPPING	0x20000000
#define	PIO2VIO(_IO_)	(_IO_-IO_BASE + MMU_VIRTUAL_ADDR_IOMAPPING)
#define GPBCON	(*(volatile unsigned long *)PIO2VIO(0x56000010))
#define GPBDAT	(*(volatile unsigned long *)PIO2VIO(0x56000014))
#define	GPBUP	(*(volatile unsigned long *)PIO2VIO(0x56000018))


GPBCON = 0x00005400;
//设置GPB7为输出口
GPBUP  = 0x7ff;
//使用上拉电阻
while(1)
GPBDAT = 0x0;
//令GPB7输出0

}*/
#endif	/* _BOOT_C */
